//===============================================================================
// Microsoft patterns & practices
// Web Client Software Factory
//-------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using DreamDriven.Components.DataSource.Utility;
using DreamDriven.SoftwareFactory.Components;
using DreamDriven.SoftwareFactory.Components.Properties;

namespace DreamDriven.Components.DataSource.Design
{
	/// <summary>
	/// Provides design-time support for the <see cref="DomainObjectDataSource"/> control.
	/// </summary>
    public sealed partial class DomainObjectDataSourceWizardForm
    {
        #region Private fields

        private readonly DomainObjectDataSourceDesigner _dataSourceControlDesigner;
        private readonly DomainObjectDataSource _dataSourceControl;
        private ITypeDiscoveryService _discoveryService;
	    private Dictionary<Type, Assembly> _typeAssemblyDictionary = new Dictionary<Type, Assembly>();

        #endregion Private fileds

        #region Nested Types

        private class TypeItem
        {
            public Type Type { get; private set; }

            public TypeItem(Type type)
            {
                Type = type;
            }

            public override string ToString()
            {
                return Type.Name;
            }
        }

        private class AssemblyItem
        {
            public Assembly Assembly { get; private set; }

            public AssemblyItem(Assembly assembly)
            {
                Assembly = assembly;
            }

            public override string ToString()
            {
                return Assembly.GetName().Name;
            }
        }

        #endregion Nested Tpes

        #region Initialization

        /// <summary>
		/// Initializes a new instance of <see cref="DomainObjectDataSourceWizardForm"/>.
		/// </summary>
		/// <param name="domainObjectDataSourceDesigner">The <see cref="DomainObjectDataSource"/> designer.</param>
        [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        public DomainObjectDataSourceWizardForm(DomainObjectDataSourceDesigner domainObjectDataSourceDesigner)
        {
            ArgumentValidator.ArgumentNotNull(domainObjectDataSourceDesigner, "domainObjectDataSourceDesigner");
            InitializeComponent();

            _dataSourceControlDesigner = domainObjectDataSourceDesigner;
            _dataSourceControl = (DomainObjectDataSource)_dataSourceControlDesigner.Component;
            Text = String.Format(CultureInfo.CurrentCulture, Resources.FormTitle, _dataSourceControl.ID);
            Initialize();
        }

	    private void Initialize()
        {
            _typeAssemblyDictionary = GetTypeAssemblyDictionary(_IsRecommendedFilterCheckBox.Checked);
            IEnumerable<Assembly> distinctAssemblies = _typeAssemblyDictionary.Values.Distinct();
            IEnumerable<AssemblyItem> assemblyItems =
                distinctAssemblies.Select(assembly => new AssemblyItem(assembly)).ToList();
            InitializeAssemblyComboBox(assemblyItems);            
        }

        #endregion Initialization

        #region Reductions

        private ITypeDiscoveryService TypeDiscoveryService
        {
            get
            {
                if (_discoveryService == null)
                {
                    if (_dataSourceControl.Site != null)
                    {
                        _discoveryService =
                            (ITypeDiscoveryService) _dataSourceControl.Site.GetService(typeof (ITypeDiscoveryService));
                    }
                }
                return _discoveryService;
            }
        }

        private string DataObjectTypeName
        {
            get { return ((TypeItem) _typeComboBox.SelectedItem).Type.FullName; }
        }

        #endregion Reductions

        #region Aux methods

        private static ICollection FilterTypes(ICollection types)
        {
            if ((types == null) || (types.Count == 0))
            {
                return types;
            }

            var filteredList = new ArrayList(types.Count);
            foreach (Type type in types)
            {
                if (!type.ContainsGenericParameters && !type.IsEnum && !type.IsInterface)
                {
                    filteredList.Add(type);
                }
            }
            return filteredList;
        }

        private void InitializeTypesComboBox(IEnumerable<TypeItem> types)
        {
            Cursor cursor = Cursor.Current;
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                _typeComboBox.Enabled = true;
                _typeComboBox.BeginUpdate();
                _typeComboBox.Items.Clear();
                foreach (TypeItem typeItem in types)
                {
                    _typeComboBox.Items.Add(typeItem);
                }
            }
            finally
            {
                _typeComboBox.EndUpdate();
                Cursor.Current = cursor;
            }
            SelectTypeInComboBox();
        }

	    private void InitializeAssemblyComboBox(IEnumerable<AssemblyItem> assemblies)
        {
            if (TypeDiscoveryService != null)
            {
                Cursor cursor = Cursor.Current;
                _typeComboBox.Enabled = false;
                try
                {
                    Cursor.Current = Cursors.WaitCursor;
                    _assemblyComboBox.BeginUpdate();
                    _assemblyComboBox.Items.Clear();
                    foreach (AssemblyItem assembly in assemblies)
                    {
                        _assemblyComboBox.Items.Add(assembly);
                    }
                }
                finally
                {
                    _assemblyComboBox.EndUpdate();
                    Cursor.Current = cursor;
                }
            }
        }

        private static IEnumerable<Type> GetAssemblyTypes(Assembly assembly, Dictionary<Type, Assembly> typeAssemblyDictionary)
        {
            return from typeAssembly in typeAssemblyDictionary 
                   where typeAssembly.Value == assembly 
                   select typeAssembly.Key;
        }

	    private Dictionary<Type, Assembly> GetTypeAssemblyDictionary(bool useRecommendedFilter)
        {
	        var dict = new Dictionary<Type, Assembly>();
            if (TypeDiscoveryService != null)
            {
                ICollection types = FilterTypes(TypeDiscoveryService.GetTypes(typeof (object), true));
                foreach (Type type in types)
                {
                    if (!useRecommendedFilter || IsImplementedDataObjectInterface(type))
                    {
                        dict.Add(type, type.Assembly);
                    }
                }
            }
            return dict;
        }

        private static bool IsImplementedDataObjectInterface(Type type)
        {
            return type.GetInterface("IDataObject") != null;
        }

        private void SelectTypeInComboBox()
        {
            foreach (TypeItem typeItem in _typeComboBox.Items)
            {
                if (typeItem.Type.FullName == _dataSourceControl.DataObjectTypeName)
                {
                    _typeComboBox.SelectedItem = typeItem;
                    return;
                }
            }
        }

	    #endregion Aux Methods

        #region Event Handlers

        private void ButtonClick(object sender, EventArgs e)
        {
            _dataSourceControlDesigner.DataObjectTypeName = DataObjectTypeName;
        }

        private void TypeNameComboBoxSelectedIndexChanged(object sender, EventArgs e)
        {
            _OKButton.Enabled = true;
        }

        private void AssemblyComboBoxSelectedIndexChanged(object sender, EventArgs e)
        {
            var assemblyItem = (AssemblyItem)_assemblyComboBox.SelectedItem;
            IEnumerable<Type> types = GetAssemblyTypes(assemblyItem.Assembly, _typeAssemblyDictionary);
            InitializeTypesComboBox(types.Select(type => new TypeItem(type)).ToList());
        }

        private void OnIsRecommendedFilterCheckedChanged(object sender, EventArgs e)
        {
            Initialize();
        }

        #endregion Event Handlers
    }
}
